package main

import (
	"strconv"
	//"unsafe"
)

type LFDataType int

const (
	LFDataType_null   LFDataType = 0x00
	LFDataType_false             = 0x10
	LFDataType_true              = 0x11
	LFDataType_u8                = 0x20
	LFDataType_u16               = 0x21
	LFDataType_u32               = 0x22
	LFDataType_u64               = 0x23
	LFDataType_s8                = 0x30
	LFDataType_s16               = 0x31
	LFDataType_s32               = 0x32
	LFDataType_s64               = 0x33
	LFDataType_f32               = 0x40
	LFDataType_f64               = 0x41
	LFDataType_string            = 0x50
	LFDataType_array             = 0x60
	LFDataType_table             = 0x70
	LFDataType_binary            = 0x80
	LFDataType_index             = 0x90
)

const (
	LFData_TYPE_NumberStart = LFDataType_u8
	LFData_TYPE_SignedStart   = LFDataType_s8
	LFData_TYPE_RealStart   = LFDataType_f32
	LFData_TYPE_NumberEnd   = LFDataType_f64
)

const (
	s8_max  = 0x7F
	s8_min  = -s8_max - 1
	s16_max = 0x7FFF
	s16_min = -s16_max - 1
	s32_max = 0x7FFFFFFF
	s32_min = -s32_max - 1
	s64_max = 0x7FFFFFFFFFFFFFFF
	s64_min = -s64_max - 1

	u8_max  = 0xFF
	u8_min  = 0
	u16_max = 0xFFFF
	u16_min = 0
	u32_max = 0xFFFFFFFF
	u32_min = 0
	u64_max = 0xFFFFFFFFFFFFFFFF
	u64_min = 0
)

type (
	LFData struct {
		dtype LFDataType
		val   interface{}
	}

	_ArrayST []*LFData
	_TableST map[string]*LFData
	_IndexST map[int64]*LFData

	_ArrayForeachCbk func(i int, lfd* LFData) bool
	_TableForeachCbk func(key string, lfd* LFData) bool
	_IndexForeachCbk func(idx int64, lfd* LFData) bool
)

func NewLFData(args ...interface{}) *LFData {
	lfd := new(LFData)
	argn := len(args)
	if argn == 0 {
		lfd.dtype = LFDataType_null
		return lfd
	} else if argn > 1 {
		panic("there is too much paras to make new LFData")
	}
	setto := args[0]
	lfd.Set(setto)
	return lfd
}

func MoveLFData(from *LFData) *LFData {
	to := new(LFData)
	to.dtype = from.dtype
	to.val = from.val
	from.Clear()
	return to
}

func Clone(lfd *LFData) *LFData {
	newlfd := NewLFData()
	newlfd.dtype = lfd.dtype
	switch lfd.dtype {
	case LFDataType_null, LFDataType_false, LFDataType_true:
		//do nothing

	case LFDataType_u8, LFDataType_u16, LFDataType_u32, LFDataType_u64, 
		 LFDataType_s8, LFDataType_s16, LFDataType_s32, LFDataType_s64, 
		 LFDataType_f32, LFDataType_f64, 
		 LFDataType_string, LFDataType_binary:
		newlfd.val = lfd.val

	case LFDataType_array:
		arr := new(_ArrayST)
		for _, v := range lfd.val.(_ArrayST) {
			*arr = append(*arr, v.CloneThis())
		}
		newlfd.val = arr

	case LFDataType_table:
		lst := make(_TableST)
		for k, v := range lfd.val.(_TableST) {
			lst[k] = v.CloneThis()
		}
		newlfd.val = &lst

	case LFDataType_index:
		lst := make(_IndexST)
		for k, v := range lfd.val.(_IndexST) {
			lst[k] = v.CloneThis()
		}
		newlfd.val = &lst
	}
	panic("there is wrong type LFData")
}

func (lfd *LFData) CloneThis() *LFData {
	return Clone(lfd)
}

func (lfd *LFData) Clear() {
	lfd.dtype = LFDataType_null
	lfd.val = nil
}

func (lfd *LFData) Eat(inVal *LFData) {
	lfd.dtype = inVal.dtype
	lfd.val = inVal.val
	inVal.Clear()
}

func (lfd *LFData) Set(setto interface{}) {
	if setto == nil {
		lfd.Clear()
		return
	}
	switch setto.(type) {
	case uint8:
		n := setto.(uint8)
		lfd.dtype = LFDataType_u8
		lfd.val = uint64(n)
	case uint16:
		n := setto.(uint16)
		if n <= u8_max {
			lfd.dtype = LFDataType_u8
		} else {
			lfd.dtype = LFDataType_u16
		}
		lfd.val = uint64(n)
	case uint32:
		n := setto.(uint32)
		if n <= u8_max {
			lfd.dtype = LFDataType_u8
		} else if n <= u16_max {
			lfd.dtype = LFDataType_u16
		} else {
			lfd.dtype = LFDataType_u32
		}
		lfd.val = uint64(n)
	case uint:
		n := setto.(uint)
		if n <= u8_max {
			lfd.dtype = LFDataType_u8
		} else if n <= u16_max {
			lfd.dtype = LFDataType_u16
		} else if n <= u32_max {
			lfd.dtype = LFDataType_u32
		} else {
			lfd.dtype = LFDataType_u64
		}
		lfd.val = uint64(n)
	case uint64:
		n := setto.(uint64)
		if n <= u8_max {
			lfd.dtype = LFDataType_u8
		} else if n <= u16_max {
			lfd.dtype = LFDataType_u16
		} else if n <= u32_max {
			lfd.dtype = LFDataType_u32
		} else {
			lfd.dtype = LFDataType_u64
		}
		lfd.val = n
	case int8:
		n := setto.(int8)
		if n >= 0 {
			lfd.dtype = LFDataType_u8
			lfd.val = uint64(n)
		} else {
			lfd.dtype = LFDataType_s8
			lfd.val = int64(n)
		}
	case int16:
		n := setto.(int16)
		if n >= 0 {
			if n <= u8_max {
				lfd.dtype = LFDataType_u8
			} else {
				lfd.dtype = LFDataType_u16
			}
			lfd.val = uint64(n)
		} else {
			if n >= s8_min {
				lfd.dtype = LFDataType_s8
			} else {
				lfd.dtype = LFDataType_s16
			}
			lfd.val = int64(n)
		}
	case int32:
		n := setto.(int32)
		if n >= 0 {
			if n <= u8_max {
				lfd.dtype = LFDataType_u8
			} else if n <= u16_max {
				lfd.dtype = LFDataType_u16
			} else {
				lfd.dtype = LFDataType_u32
			}
			lfd.val = uint64(n)
		} else {
			if n >= s8_min {
				lfd.dtype = LFDataType_s8
			} else if n >= s16_min {
				lfd.dtype = LFDataType_s16
			} else {
				lfd.dtype = LFDataType_s32
			}
			lfd.val = int64(n)
		}
	case int:
		n := setto.(int)
		if n >= 0 {
			if n <= u8_max {
				lfd.dtype = LFDataType_u8
			} else if n <= u16_max {
				lfd.dtype = LFDataType_u16
			} else if n <= u32_max {
				lfd.dtype = LFDataType_u32
			} else {
				lfd.dtype = LFDataType_u64
			}
			lfd.val = uint64(n)
		} else {
			if n >= s8_min {
				lfd.dtype = LFDataType_s8
			} else if n >= s16_min {
				lfd.dtype = LFDataType_s16
			} else if n >= s32_min {
				lfd.dtype = LFDataType_s32
			} else {
				lfd.dtype = LFDataType_s64
			}
			lfd.val = int64(n)
		}
	case int64:
		n := setto.(int64)
		if n >= 0 {
			if n <= u8_max {
				lfd.dtype = LFDataType_u8
			} else if n <= u16_max {
				lfd.dtype = LFDataType_u16
			} else if n <= u32_max {
				lfd.dtype = LFDataType_u32
			} else {
				lfd.dtype = LFDataType_u64
			}
			lfd.val = uint64(n)
		} else {
			if n >= s8_min {
				lfd.dtype = LFDataType_s8
			} else if n >= s16_min {
				lfd.dtype = LFDataType_s16
			} else if n >= s32_min {
				lfd.dtype = LFDataType_s32
			} else {
				lfd.dtype = LFDataType_s64
			}
			lfd.val = n
		}
	case string:
		lfd.dtype = LFDataType_string
		lfd.val = setto.(string)
	case float32:
		lfd.dtype = LFDataType_f32
		lfd.val = float64(setto.(float32))
	case float64:
		n := setto.(float64)
		if float64(float32(n)) == n {
			lfd.dtype = LFDataType_f32
		} else {
			lfd.dtype = LFDataType_f64
		}
		lfd.val = n
	case bool:
		if setto.(bool) {
			lfd.dtype = LFDataType_true
		} else {
			lfd.dtype = LFDataType_false
		}
	case []byte:
		lfd.dtype = LFDataType_binary
		lfd.val = setto
	case LFData:
		lfdother := setto.(LFData)
		lfd.Eat(&lfdother)
	case *LFData:
		lfdother := setto.(*LFData)
		lfd.Eat(lfdother.CloneThis())
	default:
		panic("there is a unknow type to make new LFData")
	}
}

func (lfd* LFData) fastSetUint8(n uint8) {
	lfd.dtype = LFDataType_u8
	lfd.val = uint64(n)
}

func (lfd* LFData) fastSetInt8(n int8) {
	if n >= 0 {
		lfd.dtype = LFDataType_u8
		lfd.val = uint64(n)
	} else {
		lfd.dtype = LFDataType_s8
		lfd.val = int64(n)
	}
}

func (lfd* LFData) fastSetUint64(n uint64) {
	if n <= u8_max {
		lfd.dtype = LFDataType_u8
	} else if n <= u16_max {
		lfd.dtype = LFDataType_u16
	} else if n <= u32_max {
		lfd.dtype = LFDataType_u32
	} else {
		lfd.dtype = LFDataType_u64
	}
	lfd.val = n
}

func (lfd* LFData) fastSetInt64(n int64) {
	if n >= 0 {
		if n <= u8_max {
			lfd.dtype = LFDataType_u8
		} else if n <= u16_max {
			lfd.dtype = LFDataType_u16
		} else if n <= u32_max {
			lfd.dtype = LFDataType_u32
		} else {
			lfd.dtype = LFDataType_u64
		}
		lfd.val = uint64(n)
	} else {
		if n >= s8_min {
			lfd.dtype = LFDataType_s8
		} else if n >= s16_min {
			lfd.dtype = LFDataType_s16
		} else if n >= s32_min {
			lfd.dtype = LFDataType_s32
		} else {
			lfd.dtype = LFDataType_s64
		}
		lfd.val = n
	}	
}

func (lfd* LFData) fastSetFloat32(n float32) {
	lfd.dtype = LFDataType_f32
	lfd.val = n
}

func (lfd* LFData) fastSetFloat64(n float64) {
	if float64(float32(n)) == n {
		lfd.dtype = LFDataType_f32
	} else {
		lfd.dtype = LFDataType_f64
	}
	lfd.val = n
}

func (lfd* LFData) fastSetString(str string) {
	lfd.dtype = LFDataType_string
	lfd.val = str
}

func (lfd* LFData) fastSetBinary(bin []byte) {
	lfd.dtype = LFDataType_binary
	lfd.val = bin
}

func (lfd* LFData) fastSetBool(b bool) {
	if b {
		lfd.dtype = LFDataType_true
	} else {
		lfd.dtype = LFDataType_false
	}
}

func (lfd *LFData) GetType() LFDataType {
	return lfd.dtype
}

func (lfd *LFData) IsNull() bool {
	if lfd.dtype == LFDataType_null {
		return true
	}
	return false
}

func (lfd *LFData) IsNotNull() bool {
	if lfd.dtype != LFDataType_null {
		return true
	}
	return false
}

func (lfd *LFData) IsBool() bool {
	return lfd.dtype == LFDataType_false || lfd.dtype == LFDataType_true
}

func (lfd *LFData) IsInteger() bool {
	if lfd.dtype >= LFData_TYPE_NumberStart && lfd.dtype < LFData_TYPE_RealStart {
		return true
	}
	return false
}

func (lfd *LFData) IsReal() bool {
	if lfd.dtype >= LFData_TYPE_RealStart && lfd.dtype <= LFData_TYPE_NumberEnd {
		return true
	}
	return false
}

func (lfd *LFData) IsNumber() bool {
	if lfd.dtype >= LFData_TYPE_NumberStart && lfd.dtype <= LFData_TYPE_NumberEnd {
		return true
	}
	return false
}

func (lfd *LFData) IsNumberAble() bool {
	if lfd.IsNumber() {
		return true
	}
	if lfd.dtype == LFDataType_string {
		str := lfd.val.(string)
		_, err := strconv.ParseFloat(str, 64)
		if err != nil {
			return false
		}
		return true
	}
	return false
}

func (lfd *LFData) IsString() bool {
	return lfd.dtype == LFDataType_string
}

func (lfd *LFData) IsStringAble() bool {
	return lfd.dtype < LFDataType_array
}

func (lfd *LFData) IsArray() bool {
	return lfd.dtype == LFDataType_array
}

func (lfd *LFData) IsArrayAble() bool {
	return lfd.dtype == LFDataType_null || lfd.dtype == LFDataType_array
}

func (lfd *LFData) IsTable() bool {
	return lfd.dtype == LFDataType_table
}

func (lfd *LFData) IsTableAble() bool {
	return lfd.dtype == LFDataType_null || lfd.dtype == LFDataType_table
}

func (lfd *LFData) IsBinary() bool {
	return lfd.dtype == LFDataType_binary
}

func (lfd *LFData) IsIndex() bool {
	return lfd.dtype == LFDataType_index
}

func (lfd *LFData) IsIndexAble() bool {
	return lfd.dtype == LFDataType_null || lfd.dtype == LFDataType_index
}

func (lfd *LFData) IsType(dtype LFDataType) bool {
	return lfd.dtype == dtype
}

func (lfd *LFData) IsSizeAble() bool {
	switch lfd.dtype {
	case LFDataType_null,
		LFDataType_string, LFDataType_binary,
		LFDataType_array, LFDataType_table, LFDataType_index:
		return true
	}
	return false
}

func (lfd *LFData) JustString() string {
	if !lfd.IsString() {
		panic("that LFData not String")
	}
	return lfd.val.(string)
}

func (lfd *LFData) AsString() string {
	if !lfd.IsStringAble() {
		panic("that LFData not StringAble")
	}
	switch lfd.dtype {
	case LFDataType_null:
		return "null"
	case LFDataType_false:
		return "false"
	case LFDataType_true:
		return "true"
	case LFDataType_u8, LFDataType_u16, LFDataType_u32, LFDataType_u64:
		return strconv.FormatUint(lfd.val.(uint64), 10)
	case LFDataType_s8, LFDataType_s16, LFDataType_s32, LFDataType_s64:
		return strconv.FormatInt(lfd.val.(int64), 10)
	case LFDataType_f32:
		strconv.FormatFloat(lfd.val.(float64), 'g', -1, 32)
	case LFDataType_f64:
		strconv.FormatFloat(lfd.val.(float64), 'g', -1, 64)
	case LFDataType_string:
		return lfd.val.(string)	
	}
	panic("that LFData not StringAble")
}

/*func(lfd *LFData) AsBinary() []byte{
	if !IsBinary() {
		panic("that LFData not Binary")
	}
	return *lfd.val.([]byte)
}*/

func (lfd *LFData) JustInt64() int64 {
	if !lfd.IsInteger() {
		panic("that LFData not Integer")
	}
	if lfd.dtype < LFData_TYPE_SignedStart {
		return int64(lfd.val.(uint64))
	} else {
		return lfd.val.(int64)
	}
}

func (lfd *LFData) AsInt8() int8 {
	return int8(lfd.AsInt64())
}

func (lfd *LFData) AsInt16() int16 {
	return int16(lfd.AsInt64())
}

func (lfd *LFData) AsInt32() int32 {
	return int32(lfd.AsInt64())
}

func (lfd *LFData) AsInt64() int64 {
	switch lfd.dtype {
	case LFDataType_u8, LFDataType_u16, LFDataType_u32, LFDataType_u64:
		return int64(lfd.val.(uint64))
	case LFDataType_s8, LFDataType_s16, LFDataType_s32, LFDataType_s64:
		return lfd.val.(int64)
	case LFDataType_f32, LFDataType_f64:
		return int64(lfd.val.(float64))
	case LFDataType_string:
		r,err := strconv.ParseInt(lfd.val.(string), 10, 64)
		if err!=nil {
			r,err := strconv.ParseFloat(lfd.val.(string), 64)
			if err!=nil {
				panic("that LFData not Number")
			}
			return int64(r)
		}
		return r
	}
	panic("that LFData not Number")
}

func (lfd *LFData) AsInt() int {
	return int(lfd.AsInt64())
}

func (lfd *LFData) AsUInt8() uint8 {
	return uint8(lfd.AsUInt64())
}

func (lfd *LFData) AsUInt16() uint16 {
	return uint16(lfd.AsUInt64())
}

func (lfd *LFData) AsUInt32() uint32 {
	return uint32(lfd.AsUInt64())
}

func (lfd *LFData) AsUInt64() uint64 {
	switch lfd.dtype {
	case LFDataType_u8, LFDataType_u16, LFDataType_u32, LFDataType_u64:
		return lfd.val.(uint64)
	case LFDataType_s8, LFDataType_s16, LFDataType_s32, LFDataType_s64:
		return uint64(lfd.val.(int64))
	case LFDataType_f32, LFDataType_f64:
		return uint64(lfd.val.(float64))
	case LFDataType_string:
		r,err := strconv.ParseUint(lfd.val.(string), 10, 64)
		if err!=nil {
			r,err := strconv.ParseFloat(lfd.val.(string), 64)
			if err!=nil {
				panic("that LFData not Number")
			}
			return uint64(r)
		}
		return r
	}
	panic("that LFData not Number")
}

func (lfd *LFData) AsUInt() uint {
	return uint(lfd.AsUInt64())
}

func (lfd *LFData) AsFloat32() float32 {
	return float32(lfd.AsFloat64())
}

func (lfd *LFData) AsFloat64() float64 {
	switch lfd.dtype {
	case LFDataType_u8, LFDataType_u16, LFDataType_u32, LFDataType_u64:
		return float64(lfd.val.(uint64))
	case LFDataType_s8, LFDataType_s16, LFDataType_s32, LFDataType_s64:
		return float64(lfd.val.(int64))
	case LFDataType_f32, LFDataType_f64:
		return lfd.val.(float64)
	case LFDataType_string:
		r,err := strconv.ParseFloat(lfd.val.(string), 64)
		if err!=nil {
			panic("that LFData not Number")
		}
		return r
	}
	panic("that LFData not Number")
}

func (lfd *LFData) AsFloat() float32 {
	return float32(lfd.AsFloat64())
}

func (lfd *LFData) AsDouble() float64 {
	return lfd.AsFloat64()
}

func (lfd *LFData) MakeArray() {
	if !lfd.IsArrayAble() {
		panic("that LFData not ArrayAble")
	}
	lfd.dtype = LFDataType_array
	if lfd.val == nil {
		lfd.val = new(_ArrayST)
	}
}

func (lfd *LFData) MakeTable() {
	if !lfd.IsTableAble() {
		panic("that LFData not TableAble")
	}
	lfd.dtype = LFDataType_table
	if lfd.val == nil {
		lst := make(_TableST)
		lfd.val = &lst
	}
}

func (lfd *LFData) MakeIndex() {
	if !lfd.IsIndexAble() {
		panic("that LFData not IndexAble")
	}
	lfd.dtype = LFDataType_index
	if lfd.val == nil {
		lst := make(_IndexST)
		lfd.val = &lst
	}
}

func (lfd *LFData) fastSetToArray(inVal *LFData) {
	lfd.MakeArray()
	val := append(*lfd.val.(*_ArrayST), inVal)
	lfd.val = &val 
}

func (lfd *LFData) EatToArray(inVal *LFData) {
	lfd.MakeArray()
	newlfd := MoveLFData(inVal)
	val := append(*lfd.val.(*_ArrayST), newlfd)
	lfd.val = &val
}

func (lfd *LFData) AddArray(inVal interface{}) {
	lfd.MakeArray()
	newlfd := NewLFData(inVal)
	val := append(*lfd.val.(*_ArrayST), newlfd)
	lfd.val = &val
}

func (lfd *LFData) fastSetToTable(key string, inVal *LFData) {
	lfd.MakeTable()
	vals := lfd.val.(*_TableST)
	//_, ok := (*vals)[key]
	//if ok {
	//	return false
	//}
	(*vals)[key] = inVal
	//return true
}

func (lfd *LFData) EatToTable(key string, inVal *LFData) {
	lfd.MakeTable()
	newlfd := MoveLFData(inVal)
	vals := lfd.val.(*_TableST)
	//_, ok := (*vals)[key]
	//if ok {
	//	return false
	//}
	(*vals)[key] = newlfd
	//return true
}

func (lfd *LFData) AddTable(key string, inVal interface{}) {
	lfd.MakeTable()
	newlfd := NewLFData(inVal)
	vals := lfd.val.(*_TableST)
	//_, ok := (*vals)[key]
	//if ok {
	//	return false
	//}
	(*vals)[key] = newlfd
	//return true
}

func (lfd *LFData) fastSetToIndex(index int64, inVal *LFData) {
	lfd.MakeIndex()
	vals := lfd.val.(*_IndexST)
	//_, ok := (*vals)[index]
	//if ok {
	//	return false
	//}
	(*vals)[index] = inVal
	//return true
}

func (lfd *LFData) EatToIndex(index int64, inVal *LFData) {
	lfd.MakeIndex()
	newlfd := MoveLFData(inVal)
	vals := lfd.val.(*_IndexST)
	//_, ok := (*vals)[index]
	//if ok {
	//	return false
	//}
	(*vals)[index] = newlfd
	//return true
}

func (lfd *LFData) AddIndex(index int64, inVal interface{}) {
	lfd.MakeIndex()
	newlfd := NewLFData(inVal)
	vals := lfd.val.(*_IndexST)
	//_, ok := (*vals)[index]
	//if ok {
	//	return false
	//}
	(*vals)[index] = newlfd
	//return true
}

func (lfd *LFData) FindArray(loc int) *LFData {
	if !lfd.IsArray() {
		panic("that LFData not a Array")
	}
	if loc < 0 || len(lfd.val.(_ArrayST)) <= loc {
		return nil
	}
	return (*lfd.val.(*_ArrayST))[loc]
}

func (lfd *LFData) FindTable(key string) *LFData {
	if !lfd.IsTable() {
		panic("that LFData not a Table")
	}
	return (*lfd.val.(*_TableST))[key]
}

func (lfd *LFData) FindIndex(index int64) *LFData {
	if !lfd.IsArrayAble() {
		panic("that LFData not a Index")
	}
	return (*lfd.val.(*_IndexST))[index]
}

func (lfd *LFData) ForEachArray(callback _ArrayForeachCbk) {
	if !lfd.IsArrayAble() {
		panic("that LFData not ArrayAble")
	}
	if lfd.IsNull() {
		return
	}
	for i,v := range (*lfd.val.(*_ArrayST)) {
		if !callback(i, v) {
			break
		}
	}
}

func (lfd *LFData) ForEachTable(callback _TableForeachCbk) {
	if !lfd.IsTableAble() {
		panic("that LFData not TableAble")
	}
	if lfd.IsNull() {
		return
	}
	for k,v := range (*lfd.val.(*_TableST)) {
		if !callback(k, v) {
			break
		}
	}
}

func (lfd *LFData) ForEachIndex(callback _IndexForeachCbk) {
	if !lfd.IsIndexAble() {
		panic("that LFData not IndexAble")
	}
	if lfd.IsNull() {
		return
	}
	for k,v := range (*lfd.val.(*_IndexST)) {
		if !callback(k, v) {
			break
		}
	}
}

func (lfd *LFData) Size() int{
	switch lfd.dtype {
	case LFDataType_null:
		return 0
	case LFDataType_string:
		return len(lfd.val.(string))
	case LFDataType_binary:
		return len(lfd.val.([]byte))
	case LFDataType_array:
		return len(*lfd.val.(*_ArrayST))
	case LFDataType_table:
		return len(*lfd.val.(*_TableST))
	case LFDataType_index:
		return len(*lfd.val.(*_IndexST))
	}
	panic("that LFData not SizeAble")
}

func (lfd *LFData) Empty() bool {
	return lfd.Size() == 0
}
